/*
 * Clock and PLL control for FH devices
 *
 * Copyright (C) 2014 Fullhan Microelectronics Co., Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
//#include <linux/clk-private.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/mutex.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>



static DEFINE_SPINLOCK(clk_lock);

#define	GATE_OFFSET	0x1c
#define	MAIN_RST_OFFSET	0x40
#define	AHB_RST_OFFSET	0x48
#define	APB_RST_OFFSET	0x4c

/* Maximum number of parents our clocks have */
#define FH_MAX_PARENTS	3
#define FACTORS_MAX_PARENTS     3
#define FH_MUX_GATE_WIDTH   2
#define FH_FACTORS_NOT_APPLICABLE    (0)

#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)


#define SETMASK(len, pos)       (((1U << (len)) - 1) << (pos))
#define CLRMASK(len, pos)       (~(SETMASK(len, pos)))

#define FACTOR_GET(bit, len, reg)   (((reg) & SETMASK(len, bit)) >> (bit))
#define FACTOR_SET(bit, len, reg, val) \
	    (((reg) & CLRMASK(len, bit)) | (val << (bit)))

#define CLOCK_NOGATE        	(1<<1)
#define CLOCK_NODIV         	(1<<2)
#define CLOCK_NORESET       	(1<<3)
#define CLOCK_MULTI_PARENT  	(1<<4)


/* init clk base addr */
void __iomem *base_addr;

/* Matches for factors clocks */
struct clk_factors_config {
	u8 mshift;
	u8 mwidth;
	u8 nshift;
	u8 nwidth;
	u32 pr;
	u32 divcop;
};

struct mux_data {
	u8 shift;
	u8 mask;
	u8 gate;
};


struct factors_data {
	int enable;
	struct clk_factors_config *table;
	const char *name;
};

struct clk_factors {
	struct clk_hw hw;
	void __iomem *reg0;	
	void __iomem *reg1;
	struct clk_factors_config *config;
	spinlock_t *lock;
};

struct divs_data {
	u32 prediv;
	u32 flag;	
	u32 div_reg_mask; 
	u32 en_reg_mask; 
	u32 sel_reg_mask;   
	u32 rst_reg_mask; 
	struct clk_div_table *table; /* is it a table based divisor? */
};


struct fh_clk_divider {
	struct clk_hw	hw;
	void __iomem	*reg0;
	void __iomem	*reg1;
	const struct divs_data *config;
	spinlock_t	*lock;
};

struct fh_clk_phase {
	struct clk_hw	hw;
	void __iomem	*reg;
	struct mux_data *config;
	spinlock_t	*lock;
};



static struct clk_factors_config fh_pll_config = {
	.mshift = 0,
	.mwidth = 7,
	.nshift = 8,
	.nwidth = 5,
};

static struct factors_data fh_pll_data __initdata = {
	.enable = 0,
	.table = &fh_pll_config,
};

struct fh_clk_config {
	char	name[32];
	u32	    rate;
};


/*
 * pll0r
 */

static struct divs_data divs_arm_data __initdata = {
    .flag	            = CLOCK_NOGATE,
	.prediv             = 1,
	.div_reg_mask       = 0xf,
	.rst_reg_mask       = 0x1,
};

static struct divs_data divs_arc_data __initdata = {
	.prediv             = 1,
	.div_reg_mask		= 0xf0000,
    .en_reg_mask        = 0x4,
    .rst_reg_mask       = 0x400000,
};

static struct divs_data divs_ahb_data __initdata = {
	.flag               = CLOCK_NORESET|CLOCK_NOGATE,
	.prediv             = 2,
	.div_reg_mask       = 0xf0000,
};

static struct divs_data divs_pae_arc_data __initdata = {
	.prediv = 1,
	.div_reg_mask       = 0xf0000,
	.en_reg_mask        = 0x8,
	.rst_reg_mask       = 0x400000,
};

static struct divs_data divs_cisout_data __initdata = {
	.flag               = CLOCK_NORESET,
	.prediv = 1,
	.div_reg_mask       = 0xff0000,
	.en_reg_mask        = 0x800000,
};

/*
 * pll1r
 */

static struct divs_data divs_ddr_data __initdata = {
	.prediv = 1,
	.div_reg_mask       = 0xf,
	.en_reg_mask        = 0x40,
	.rst_reg_mask       = 0x8,
};

/*
 * pll0p
 */

static struct divs_data divs_isp_data __initdata = {
	.flag				= CLOCK_NORESET,
	.prediv = 1,
	.div_reg_mask		= 0xf00,
	.en_reg_mask        = 0x1,

};

static struct divs_data divs_jpeg_data __initdata = {
	.flag				= CLOCK_NORESET|CLOCK_NODIV,
	.prediv = 1,
	.en_reg_mask		= 0x1,

};

static struct divs_data divs_vpu_data __initdata = {
	.flag				= CLOCK_NORESET|CLOCK_NODIV,
	.prediv = 1,
	.en_reg_mask		= 0x1,

};


static struct divs_data divs_bgm_data __initdata = {
	.flag				= CLOCK_NORESET,
	.prediv = 1,
	.div_reg_mask       = 0xf00,
	.en_reg_mask        = 0x40000,
};

static struct divs_data divs_mpdphy_data __initdata = {
	.flag				= CLOCK_NORESET|CLOCK_NODIV,
	.prediv = 1,
	.en_reg_mask		= 0x100000,

};


static struct divs_data fh_pix_clk_data __initdata = {
	.flag               = CLOCK_NORESET,
	.prediv = 1,
	.div_reg_mask		= 0xf000000,
	.en_reg_mask		= 0x400000,


};


static struct divs_data divs_pae_data __initdata = {
	.flag               = CLOCK_NORESET|CLOCK_MULTI_PARENT,
	.prediv = 1,
	.div_reg_mask		= 0x7000000,
	.sel_reg_mask		= 0x2,
	.en_reg_mask		= 0x10,



};

static struct divs_data divs_ddrc_a1clk_data __initdata = {
	.flag               = CLOCK_NORESET|CLOCK_NODIV,
	.prediv = 1,
	.en_reg_mask		= 0x20,

};

/*
 * pll2p
 */

static struct divs_data divs_hevc_aclk_data __initdata = {
	.flag               = CLOCK_MULTI_PARENT,
	.prediv = 1,
	.div_reg_mask       = 0x700,
	.sel_reg_mask       = 0x1,
	.en_reg_mask        = 0x20000000,
	.rst_reg_mask		= 0x1000000,

};

static struct divs_data divs_hevc_bclk_data __initdata = {
	.flag               = CLOCK_MULTI_PARENT,
	.prediv = 1,
	.div_reg_mask       = 0x70000,
	.sel_reg_mask       = 0x1,
	.en_reg_mask        = 0x40000000,
	.rst_reg_mask		= 0x2000000,

};

static struct divs_data divs_hevc_cclk_data __initdata = {
	.flag               = CLOCK_MULTI_PARENT,
	.prediv = 1,
	.div_reg_mask       = 0x7000000,
	.sel_reg_mask       = 0x1,
	.en_reg_mask        = 0x80000000,
	.rst_reg_mask		= 0x4000000,

};

/*
 * pll0p
 */

static struct divs_data divs_div12_dw_data __initdata = {
	.flag				= CLOCK_NORESET|CLOCK_NOGATE,
	.prediv = 1,
	.div_reg_mask		= 0xf000000,

};

static struct divs_data divs_sdc0_data __initdata = {
	.prediv = 2,
	.div_reg_mask		= 0xf00,
	.en_reg_mask		= 0x200,
	.rst_reg_mask		= 0x4,

};

static struct divs_data divs_sdc1_data __initdata = {
	.prediv = 2,
	.div_reg_mask		= 0xf000000,
	.en_reg_mask        = 0x400,
	.rst_reg_mask       = 0x2,

};


static struct divs_data divs_spi0_data __initdata = {
	.prediv = 1,
	.div_reg_mask		= 0xff,
	.en_reg_mask        = 0x80,
	.rst_reg_mask       = 0x100,

};

static struct divs_data divs_spi1_data __initdata = {
	.prediv = 1,
	.div_reg_mask		= 0xff0000,
	.en_reg_mask        = 0x100,
	.rst_reg_mask       = 0x200,

};

static struct divs_data divs_spi2_data __initdata = {
	.prediv = 1,
	.div_reg_mask       = 0xf000,
	.en_reg_mask        = 0x2,
	.rst_reg_mask       = 0x100000,
};

static struct divs_data divs_eth_data __initdata = {
	.prediv = 1,
	.div_reg_mask       = 0xf000000,
	.en_reg_mask        = 0x12000000,
	.rst_reg_mask       = 0x20000,
};

static struct divs_data divs_i2c0_data __initdata = {
	.prediv = 1,
	.div_reg_mask       = 0x3f0000,
	.en_reg_mask        = 0x1000,
	.rst_reg_mask       = 0x400,
};

static struct divs_data divs_i2c1_data __initdata = {
	.prediv = 1,
	.div_reg_mask       = 0x3f000000,
	.en_reg_mask        = 0x8000000,
	.rst_reg_mask       = 0x800,
};

static struct divs_data divs_pwm_data __initdata = {
	.prediv = 1,
	.div_reg_mask       = 0xff,
	.en_reg_mask        = 0x10000,
	.rst_reg_mask       = 0x80,
};

static struct divs_data divs_uart0_data __initdata = {
	.prediv = 1,
	.div_reg_mask		= 0x1f,
	.en_reg_mask		= 0x2000,
	.rst_reg_mask		= 0x4000,

};

static struct divs_data divs_uart1_data __initdata = {
	.prediv = 1,
	.div_reg_mask       = 0x1f00,
	.en_reg_mask        = 0x4000,
	.rst_reg_mask       = 0x8000,
};
static struct divs_data divs_pts_data __initdata = {
	.prediv = 1,
	.div_reg_mask		= 0x1ff,
	.en_reg_mask        = 0x80000,
	.rst_reg_mask       = 0x20000,

};

static struct divs_data divs_efuse_data __initdata = {
	.prediv = 1,
    .div_reg_mask       = 0x3f000000,
    .en_reg_mask        = 0x200000,
    .rst_reg_mask       = 0x800000,
};

static struct divs_data divs_tmr0_data __initdata = {
	.prediv = 1,
	.div_reg_mask       = 0xff0000,
	.en_reg_mask        = 0x20000,
	.rst_reg_mask       = 0x40000,
};

static struct divs_data divs_ac_data __initdata = {
	.prediv = 1,
	.div_reg_mask       = 0x3f,
	.en_reg_mask        = 0x800,
	.rst_reg_mask       = 0x1000,
};

static struct divs_data divs_i2s_data __initdata = {
	.prediv = 1,
	.div_reg_mask       = 0x3f00,
	.en_reg_mask        = 0x1000000,
	.rst_reg_mask       = 0x2000,
};

static struct divs_data divs_sadc_data __initdata = {
	.prediv = 1,
	.div_reg_mask		= 0x7f0000,
	.en_reg_mask        = 0x4000000,
	.rst_reg_mask       = 0x10000,

};
static struct divs_data divs_wdt_data __initdata = {
	.flag               = CLOCK_NOGATE,
	.prediv = 1,
	.div_reg_mask		= 0xff00,
	.rst_reg_mask       = 0x100000,

};

static struct mux_data fh_sdc0_sample_data __initdata = {
	.shift = 16,
	.mask = 2,
};
static struct mux_data fh_sdc0_drv_data __initdata = {
	.shift = 20,
	.mask = 2,
};

static struct mux_data fh_sdc1_sample_data __initdata = {
	.shift = 8,
	.mask = 2,
};
static struct mux_data fh_sdc1_drv_data __initdata = {
	.shift = 12,
	.mask = 2,
};



static struct of_device_id clk_fix_match[] __initdata = {
	{.compatible = "fixed-clock", .data = NULL,},
	{}
};

void __init fh_fixed_clk_setup(struct device_node *node,
						void *data)
{
	struct clk *clk;
	u32 out_values;
	const char *clk_name = node->name;

	of_property_read_string(node, "clock-output-names", &clk_name);
	of_property_read_u32_array(node,
			       "clock-frequency", &out_values,
			       1);
	clk = clk_register_fixed_rate(NULL, clk_name,
				      NULL,
				      CLK_IS_ROOT,
				      out_values);
	if (!IS_ERR(clk)) {
		of_clk_add_provider(node, of_clk_src_simple_get, clk);
		clk_register_clkdev(clk, clk_name, NULL);
	}
}

#define   DIVVCO_ONE_DEVISION          0x0
#define   DIVVCO_TWO_DEVISION          0x8
#define   DIVVCO_FOUR_DEVISION         0xc
#define   DIVVCO_EIGHT_DEVISION        0xd
#define   DIVVCO_SIXTEEN_DEVISION      0xe
#define   DIVVCO_THIRTYTWO_DEVISION    0xf

static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
						unsigned long parent_rate)
{
	unsigned int m = 0, n = 0, pr = 1;
	unsigned int clk_vco, divvcop = 1, shift;
	u32 reg;
	unsigned long rate;
	struct clk_factors *factors = to_clk_factors(hw);
	struct clk_factors_config *config = factors->config;
	unsigned int divcop = 0;

	/* Fetch the register value */
	reg = readl(factors->reg0);

	/* Get each individual factor if applicable */
	if (config->mwidth != FH_FACTORS_NOT_APPLICABLE)
		m = FACTOR_GET(config->mshift, config->mwidth, reg);
	if (config->nwidth != FH_FACTORS_NOT_APPLICABLE)
		n = FACTOR_GET(config->nshift, config->nwidth, reg);
	
	/*pll databook*/
	if(m<4)
	    m=128+m;

	if(m==0xb)
	    m=0xa;

	shift = ffs(config->pr)-1;
	pr = (reg&config->pr) >> shift;
	
	reg = readl(factors->reg1);
    shift = ffs(config->divcop)-1;
	divcop = ((reg&config->divcop) >> shift);
	

	/* Calculate the rate */
	switch (divcop){
	case DIVVCO_ONE_DEVISION:
		divvcop=1;
		break;

	case DIVVCO_TWO_DEVISION:
		divvcop=2;
		break;

	case DIVVCO_FOUR_DEVISION:
		divvcop=4;
		break;

	case DIVVCO_EIGHT_DEVISION:
		divvcop=8;
		break;

	case DIVVCO_SIXTEEN_DEVISION:
		divvcop=16;
		break;

	case DIVVCO_THIRTYTWO_DEVISION:
		divvcop=32;
		break;
	default:
		printk("divvcop error:%x\n",divvcop);
	}

	clk_vco = parent_rate * m / (n+1);
	rate = clk_vco/ (pr+1)/divvcop;
	return rate;

}

static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
					unsigned long *parent_rate)
	{
		unsigned int m =0, n = 0, pr = 1;
		unsigned int clk_vco, divvcop = 1, shift;
		u32 reg;
		struct clk_factors *factors = to_clk_factors(hw);
		struct clk_factors_config *config = factors->config;
		unsigned int divcop = 0;
	
		/* Fetch the register value */
		reg = readl(factors->reg0);
	
		/* Get each individual factor if applicable */
		if (config->mwidth != FH_FACTORS_NOT_APPLICABLE)
			m = FACTOR_GET(config->mshift, config->mwidth, reg);
		if (config->nwidth != FH_FACTORS_NOT_APPLICABLE)
			n = FACTOR_GET(config->nshift, config->nwidth, reg);
		
		/*pll databook*/
		if(m<4)
			m = 128+m;
	
		if(m==0xb)
			m = 0xa;
	
		shift = ffs(config->pr)-1;
		pr = (reg&config->pr) >> shift;
		
		reg = readl(factors->reg1);
		shift = ffs(config->divcop)-1;
		divcop = (reg&config->divcop) >> shift;
		
	
		/* Calculate the rate */
		switch (divcop){
		case DIVVCO_ONE_DEVISION:
			divvcop=1;
			break;
	
		case DIVVCO_TWO_DEVISION:
			divvcop=2;
			break;
	
		case DIVVCO_FOUR_DEVISION:
			divvcop=4;
			break;
	
		case DIVVCO_EIGHT_DEVISION:
			divvcop=8;
			break;
	
		case DIVVCO_SIXTEEN_DEVISION:
			divvcop=16;
			break;
	
		case DIVVCO_THIRTYTWO_DEVISION:
			divvcop=32;
			break;
		default:
			printk("divvcop error:%x\n",divvcop);
		}
	
		clk_vco = (*parent_rate) * m / (n+1);
		rate = clk_vco/ (pr+1)/divvcop;
		return rate;
	
	}


static const struct clk_ops clk_factors_ops = {
	.recalc_rate = clk_factors_recalc_rate,
	.round_rate = clk_factors_round_rate,
//	.set_rate = clk_factors_set_rate,
};

static unsigned long fh_clk_divide_recalc_rate(struct clk_hw *hw,
						unsigned long parent_rate)
{
	u32 reg;
	u32 divide = 0;
	struct fh_clk_divider *factors = (struct fh_clk_divider *)hw;
	const struct divs_data      *config = factors->config;
	unsigned long rate;
	unsigned long shift;
	unsigned long flags = 0;

	if (factors->lock)
		spin_lock_irqsave(factors->lock, flags);
	if (!(config->flag&CLOCK_NODIV)) {
		/* Fetch the register value */
		reg = readl(factors->reg0);

		/* Get each individual factor if applicable */
		shift = ffs(config->div_reg_mask)-1;
		divide = (config->div_reg_mask&reg)>> shift;

		/* Calculate the rate */
		rate = (parent_rate) / (divide+1) / config->prediv;
	} else
		rate = parent_rate / config->prediv;
	if (factors->lock)
		spin_unlock_irqrestore(factors->lock, flags);

	return rate;
}

static long fh_clk_divide_round_rate(struct clk_hw *hw, unsigned long rate,
					unsigned long *parent_rate)
{
	u32 reg;
	u32 rount_rate = 0, divide;
	struct fh_clk_divider *factors = (struct fh_clk_divider *)hw;
	const struct divs_data      *config = factors->config;
	unsigned long flags = 0;

	if (factors->lock)
		spin_lock_irqsave(factors->lock, flags);

	if (!(config->flag&CLOCK_NODIV)) {

			/* Fetch the register value */
			reg = readl(factors->reg0);

			/* Calculate the rate */
			divide = (*parent_rate) / config->prediv / rate;
			rount_rate = (*parent_rate) / divide / config->prediv;
		} else
		pr_err("fh_clk_divide_round_rate not support divide\n");

	if (factors->lock)
		spin_unlock_irqrestore(factors->lock, flags);

	return rount_rate;
}

static int fh_clk_divide_set_rate(struct clk_hw *hw, unsigned long rate,
						unsigned long parent_rate)
{
	u32 reg;
	struct fh_clk_divider *factors = (struct fh_clk_divider *)hw;
	const struct divs_data      *config = factors->config;
	u32 divide = 0;
	unsigned long flags = 0;
	unsigned long shift = 0;

	if (factors->lock)
		spin_lock_irqsave(factors->lock, flags);


	if (!(config->flag&CLOCK_NODIV)) {

			/* Fetch the register value */
			reg = readl(factors->reg0);

			divide = parent_rate/rate/config->prediv - 1;
			printk("fh_clk_divide_set_rate:%x\n",divide);

			shift = ffs(config->div_reg_mask)-1;
			

			reg &= ~(config->div_reg_mask);
	        reg |= (divide << shift);

			/* Apply them now */
			clk_writel(reg, factors->reg0);

		}
	if (factors->lock)
		spin_unlock_irqrestore(factors->lock, flags);

	return 0;
}


static const struct clk_ops fh_clk_divider_ops = {
	.recalc_rate = fh_clk_divide_recalc_rate,
	.round_rate = fh_clk_divide_round_rate,
	.set_rate = fh_clk_divide_set_rate,
};


/* Matches for factors clocks */
static struct of_device_id clk_factors_match[] __initdata = {
	{.compatible = "fh,fh-pll-clk", .data = &fh_pll_data,},
	{}
};

static int  __init fh_divs_clk_setup(struct device_node *node,
			const struct divs_data *data)
{
	struct  clk *clk;
	struct  clk_gate *gate = NULL;
	struct  clk_divider *divider = NULL;
	struct  clk_hw   *clk_mux_hw = NULL;
	struct  clk_mux  *mux = NULL;
	struct  clk_hw *clk_gate_hw = NULL;
	struct  clk_hw *clk_divider_hw = NULL;
	struct  fh_clk_divider *fh_divider = NULL;
	const char *clk_name = node->name;
	const char *parents[FACTORS_MAX_PARENTS];
	void __iomem *reg;
	int i = 0;
	//int flags = 0;


	if (!base_addr)
		return 0;

	/* if we have a mux, we will have >1 parents */
	while (i < FACTORS_MAX_PARENTS &&
			(parents[i] = of_clk_get_parent_name(node, i)) != NULL)
		i++;

	of_property_read_string(node, "clock-output-names", &clk_name);
	

	fh_divider = kzalloc(sizeof(*fh_divider), GFP_KERNEL);
	if (!fh_divider)
		return 0;
	fh_divider->config = data;
	fh_divider->lock = &clk_lock;

	/* Add a gate if this factor clock can be gated */
	if (!(data->flag&CLOCK_NOGATE)) {
		gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
		if (!gate)
			{
			kfree(fh_divider);
			return 0;
			}

		reg = base_addr + GATE_OFFSET;

		/* set up gate properties */
		gate->reg = reg;
		gate->bit_idx =ffs(data->en_reg_mask)-1;
		gate->lock = &clk_lock;
		gate->flags = CLK_GATE_SET_TO_DISABLE;
		clk_gate_hw = &gate->hw;
	}

	/* Leaves can be fixed or configurable divisors */
	if (!(data->flag&CLOCK_NODIV)) {
		divider = kzalloc(sizeof(*divider), GFP_KERNEL);
		if (!divider) {
			kfree(gate);
			return 0;
		}
		reg = of_iomap(node, 0);
		//flags = 0;

		fh_divider->reg0 = reg;
		//divider->reg = reg;
		//divider->shift = ffs(data->div_reg_mask)-1;
		//divider->width = ffs(data->div_reg_mask>>divider->shift+1)-1;
		//divider->flags = flags;
		divider->lock = &clk_lock;
		clk_divider_hw = &fh_divider->hw;
	}

	if (data->flag&CLOCK_MULTI_PARENT) {
		mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);

		reg = of_iomap(node, 1);
		fh_divider->reg1 = reg;
		/* set up gate properties */
		mux->reg = reg;
		mux->shift = ffs(data->sel_reg_mask)-1;
		mux->mask = data->sel_reg_mask>>mux->shift;
		mux->lock = &clk_lock;
		clk_mux_hw = &mux->hw;
	}
#if 1
	clk = clk_register_composite(NULL, clk_name,
			parents, i,
			clk_mux_hw, &clk_mux_ops,
			clk_divider_hw, &fh_clk_divider_ops,
			clk_gate_hw, &clk_gate_ops, CLK_IGNORE_UNUSED);

	if (!IS_ERR(clk)) {
		of_clk_add_provider(node, of_clk_src_simple_get, clk);
		clk_register_clkdev(clk, clk_name, NULL);
	}
#endif
	return 1;
}

/* Matches for divided outputs */
static struct of_device_id clk_divs_match[] __initdata = {
	{.compatible = "fh,fh-arm-clk", .data = &divs_arm_data,},
	{.compatible = "fh,fh-arc-clk", .data = &divs_arc_data,},
	{.compatible = "fh,fh-ahb-clk", .data = &divs_ahb_data,},
	{.compatible = "fh,fh-pae-arc-clk", .data = &divs_pae_arc_data,},
	{.compatible = "fh,fh-ddr-clk", .data = &divs_ddr_data,},
	{.compatible = "fh,fh-isp-clk", .data = &divs_isp_data,},
	{.compatible = "fh,fh-jpeg-clk", .data = &divs_jpeg_data,},
	{.compatible = "fh,fh-vpu-clk", .data = &divs_vpu_data,},
	{.compatible = "fh,fh-pae-clk", .data = &divs_pae_data,},
	{.compatible = "fh,fh-bgm-clk", .data = &divs_bgm_data,},
	{.compatible = "fh,fh-ddrc-a1clk", .data = &divs_ddrc_a1clk_data,},
	{.compatible = "fh,fh-hevc-aclk", .data = &divs_hevc_aclk_data,},
	{.compatible = "fh,fh-hevc-bclk", .data = &divs_hevc_bclk_data,},
	{.compatible = "fh,fh-hevc-cclk", .data = &divs_hevc_cclk_data,},
	{.compatible = "fh,fh-div12-dw-clk", .data = &divs_div12_dw_data,},
	{.compatible = "fh,fh-cisout-clk", .data = &divs_cisout_data,},
	{.compatible = "fh,fh-mpdphy-clk", .data = &divs_mpdphy_data,},
	{.compatible = "fh,fh-pts-clk", .data = &divs_pts_data,},
	{.compatible = "fh,fh-spi0-clk", .data = &divs_spi0_data,},
	{.compatible = "fh,fh-spi1-clk", .data = &divs_spi1_data,},
	{.compatible = "fh,fh-spi2-clk", .data = &divs_spi2_data,},
	{.compatible = "fh,fh-uart0-clk", .data = &divs_uart0_data,},
	{.compatible = "fh,fh-uart1-clk", .data = &divs_uart1_data,},
	{.compatible = "fh,fh-i2c0-clk", .data = &divs_i2c0_data,},
	{.compatible = "fh,fh-i2c1-clk", .data = &divs_i2c1_data,},
	{.compatible = "fh,fh-pwm-clk", .data = &divs_pwm_data,},
	{.compatible = "fh,fh-tmr0-clk", .data = &divs_tmr0_data,},
	{.compatible = "fh,fh-ac-clk", .data = &divs_ac_data,},
	{.compatible = "fh,fh-i2s-clk", .data = &divs_i2s_data,},
	{.compatible = "fh,fh-sadc-clk", .data = &divs_sadc_data,},
	{.compatible = "fh,fh-efuse-clk", .data = &divs_efuse_data,},
	{.compatible = "fh,fh-sdc0-clk", .data = &divs_sdc0_data,},
	{.compatible = "fh,fh-sdc1-clk", .data = &divs_sdc1_data,},
	{.compatible = "fh,fh-eth-clk", .data = &divs_eth_data,},
	{.compatible = "fh,fh-wdt-clk", .data = &divs_wdt_data,},
	{.compatible = "fh,fh-pix-clk", .data = &fh_pix_clk_data,},
	{}
};


static int __init fh_factors_clk_setup(struct device_node *node,
			const struct factors_data *data)
{
	struct clk *clk;
	struct clk_factors *factors;
	struct clk_hw *gate_hw = NULL;
	const char *clk_name = node->name;
	const char *parents[FACTORS_MAX_PARENTS];
	void __iomem *reg0;
	void __iomem *reg1;
	int i = 0;
	u32 pr = 0;
	u32 divcop = 0;

	if (!base_addr)
		return 0;

	reg0 = of_iomap(node, 0);
	reg1 = of_iomap(node, 1);
	/* if we have a mux, we will have >1 parents */
	while (i < FACTORS_MAX_PARENTS &&
			(parents[i] = of_clk_get_parent_name(node, i)) != NULL)
		i++;

	of_property_read_string(node, "clock-output-names", &clk_name);

	factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
	if (!factors)
		return 0;

    of_property_read_u32(node, "pr", &pr);
	data->table->pr = pr;

	of_property_read_u32(node, "divcop", &divcop);
	data->table->divcop = divcop;
	
	/* set up factors properties */
	factors->reg0 = reg0;
	factors->reg1 = reg1;
	factors->config = data->table;
	factors->lock = &clk_lock;


	clk = clk_register_composite(NULL, clk_name,
			parents, i,
			NULL, NULL,
			&factors->hw, &clk_factors_ops,
			gate_hw, &clk_gate_ops, CLK_IGNORE_UNUSED);

	if (!IS_ERR(clk)) {
		of_clk_add_provider(node, of_clk_src_simple_get, clk);
		clk_register_clkdev(clk, clk_name, NULL);
	}

	return 1;
}


/* Matches for phase clocks */

static struct of_device_id clk_phase_match[] __initdata = {
	{.compatible = "fh,fh-sdc0-clk_sample", .data = &fh_sdc0_sample_data,},
	{.compatible = "fh,fh-sdc0-clk_drv", .data = &fh_sdc0_drv_data,},
	{.compatible = "fh,fh-sdc1-clk_sample", .data = &fh_sdc1_sample_data,},
	{.compatible = "fh,fh-sdc1-clk_drv", .data = &fh_sdc1_drv_data,},
	{}
};


static int fh_clk_set_phase(struct clk_hw *hw,
						int degree)
{
	u32 reg;
	struct fh_clk_phase *phase = (struct fh_clk_phase *)hw;
	struct mux_data      *config = phase->config;
	unsigned long flags = 0;
	u32 local_degree = 0;

	if (phase->lock)
		spin_lock_irqsave(phase->lock, flags);

		/* Fetch the register value */
		reg = readl(phase->reg);

		local_degree = degree / 90;

		reg = FACTOR_SET(config->shift, config->mask, reg,
			local_degree);

		/* Apply them now */
		writel(reg, phase->reg);
	if (phase->lock)
		spin_unlock_irqrestore(phase->lock, flags);

	return 1;
}

static int fh_clk_get_phase(struct clk_hw *hw)
{
	u32 reg;
	struct fh_clk_phase *phase = (struct fh_clk_phase *)hw;
	struct mux_data      *config = phase->config;
	unsigned long flags = 0;
	u32 local_degree = 0;

	if (phase->lock)
		spin_lock_irqsave(phase->lock, flags);

		/* Fetch the register value */
		reg = readl(phase->reg);

		reg = FACTOR_GET(config->shift, config->mask, reg);
		local_degree = reg * 90;

	if (phase->lock)
		spin_unlock_irqrestore(phase->lock, flags);

	return local_degree;
}


static const struct clk_ops fh_clk_phase_ops = {
	.set_phase = fh_clk_set_phase,
	.get_phase = fh_clk_get_phase,
};

static int __init fh_phase_clk_setup(struct device_node *node,
						struct mux_data *data)
{
	struct clk *clk;
	const char *clk_name = node->name;
	const char *parents[FH_MAX_PARENTS];
	void __iomem *reg;
	struct fh_clk_phase *clk_phase = NULL;
	int i = 0;
	struct clk_init_data *init = NULL;

	while (i < FH_MAX_PARENTS &&
			(parents[i] = of_clk_get_parent_name(node, i)) != NULL)
		i++;

	of_property_read_string(node, "clock-output-names", &clk_name);

	clk_phase = kzalloc(sizeof(struct fh_clk_phase), GFP_KERNEL);
	if (!clk_phase)
		return 0;
	init = kzalloc(sizeof(struct clk_init_data), GFP_KERNEL);

	reg = of_iomap(node, 0);
	/* set up gate properties */
	clk_phase->reg = reg;
	clk_phase->config = data;
	clk_phase->lock = &clk_lock;
	init->ops = &fh_clk_phase_ops;
	init->parent_names = parents;
	init->num_parents = i;
	init->name = clk_name;
	clk_phase->hw.init = init;
	clk = clk_register(NULL, &clk_phase->hw);

	if (clk) {
		of_clk_add_provider(node, of_clk_src_simple_get, clk);
		clk_register_clkdev(clk, clk_name, NULL);
	}

	return 1;
}


static void __init of_fh_table_clock_setup(const struct of_device_id *clk_match,
								void *function)
{
	struct device_node *np;
	const struct div_data *data;
	const struct of_device_id *match;
	int (*setup_function)(struct device_node *, const void *) = function;

	for_each_matching_node_and_match(np, clk_match, &match) {
		data = match->data;
		setup_function(np, data);
	}
}


static int __init fh_clk_setup(struct device_node *node,
				void *data)
{
	if (!base_addr)
		base_addr = of_iomap(node, 0);

	return 1;
}

/* init clocks base*/
static struct of_device_id clk_init_match[] __initdata = {
	{.compatible = "fh,fh-clk", .data = NULL,},
	{}
};

static void __init fh_init_setup(struct fh_clk_config clocks[], int nclocks)
{
	int i = 0;

	/* init clocks base*/
	of_fh_table_clock_setup(clk_init_match, fh_clk_setup);

	/* Register fixed clocks */
	of_fh_table_clock_setup(clk_fix_match, fh_fixed_clk_setup);

	/* Register factor clocks */
	of_fh_table_clock_setup(clk_factors_match, fh_factors_clk_setup);

	/* Register divided output clocks */
	of_fh_table_clock_setup(clk_divs_match, fh_divs_clk_setup);

	/* Register phase clocks */
	of_fh_table_clock_setup(clk_phase_match, fh_phase_clk_setup);

	/* Protect the clocks that needs to stay on */
	for (i = 0; i < nclocks; i++) {
		struct clk *clk = clk_get(NULL, clocks[i].name);

		if (!IS_ERR(clk))
			clk_set_rate(clk,clocks[i].rate);
		
		if (!IS_ERR(clk))
			clk_prepare_enable(clk);
	}
}

static struct fh_clk_config fh_critical_clocks[] __initconst = {
	{.name="pae_clk",.rate=400000000,},
	{.name="hevc_aclk",.rate=300000000,},
	{.name="hevc_bclk",.rate=300000000,},
	{.name="hevc_cclk",.rate=300000000,},
	{.name="isp_aclk", .rate=200000000,}
};

static void __init fh_init_clocks(struct device_node *node)
{
	fh_init_setup(fh_critical_clocks,
			ARRAY_SIZE(fh_critical_clocks));
}

CLK_OF_DECLARE(fh_clk_init, "fh,fh-clk", fh_init_clocks);

